﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using Newtonsoft.Json.Linq;
using System.Security.Cryptography;
using System.Text;

namespace 视频流媒体管理.Code
{
    public class 摄像机管理
    {
        public static 摄像机管理 ONE = new 摄像机管理();
        public DataTable dt摄像机列表;
        public string host;  //easydarwin的api地址
        public string token = ""; //登录到easydarwin的token
        public int 保活超时分钟数 = 0;
        private System.Collections.Concurrent.ConcurrentDictionary<string, DateTime> dict播放列表 = new System.Collections.Concurrent.ConcurrentDictionary<string, DateTime>();
        private System.Timers.Timer tmr = new System.Timers.Timer();

        private 摄像机管理()
        {
            this.tmr.Elapsed += Tmr_Elapsed;
            this.tmr.Interval = 60 * 1000;
            this.tmr.Enabled = true;
        }

        private void Tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                DataTable dt = this.取得直播列表();
                foreach (DataRow dr in dt.Rows)
                {
                    if ( (DateTime.Now - (DateTime)dr["保活时间"]).TotalMinutes > this.保活超时分钟数)
                    {
                        string id = dr["id"].ToString();
                        JObject p = new JObject();
                        p["id"] = id;
                        this.删除直播(p.ToString());
                        log.ONE.写日志("保活时间到，删除直播：" + id);
                    }

                }
            }
            catch (Exception ex)
            {
                log.ONE.写日志("Tmr_Elapsed：" + ex.Message);
            }
        }

        public void 加载摄像机列表xls(string xls)
        {
            //序号 摄像机ID   摄像机名称 直播地址    备注1 备注2
            //1   A100 通信机房    rtsp://admin:yyJKW1100@10.136.130.100:554/Streaming/Channels/101		

            this.dt摄像机列表 = MiniExcelLibs.MiniExcel.QueryAsDataTable(xls, true);
            for (int m = this.dt摄像机列表.Rows.Count - 1; m >= 0; m--)
            {
                DataRow dr = this.dt摄像机列表.Rows[m];
                if (dr["摄像机ID"].ToString() == "")
                    dr.Delete();
            }
            this.dt摄像机列表.AcceptChanges(); //删除空行
        }

        public string 登录(string username, string pwd)
        {
            try
            {
                pwd = this.生成MD5(pwd).ToLower(); //口令用md5加密，小写
                string url = this.host + "/login";            //https://blog.csdn.net/qq_51860400/article/details/132455026

                Dictionary<string, string> dict = new Dictionary<string, string>();
                dict["username"] = username;
                dict["password"] = pwd;
                dict["onlytoken"] = "true";

                视频流媒体管理.Code.HttpPost hp = new Code.HttpPost();
                string cons = hp.组合FormData(dict, out string contType);
                string str = hp.PostData(url, contType, cons, new Dictionary<string, string>());

                // System.IO.File.AppendAllText(logfile, DateTime.Now.ToString() + str);
                if (str.Contains("token") == true)
                {
                    JObject t = JObject.Parse(str);
                    this.token = t["token"].ToString();
                    return "登录成功";
                }
                return "登录失败:" + str;
            }
            catch (Exception ex)
            {
                return "登录失败:" + ex.Message;
            }
        }

        public void 退出登录()
        {
            try
            {
                if (token != "")
                {
                    string url = this.host + "/logout?token=" + this.token;
                    视频流媒体管理.Code.HttpPost hp = new Code.HttpPost();
                    string str = hp.GetData(url, "", new Dictionary<string, string>());
                    // System.IO.File.AppendAllText(logfile, "退出登录" + DateTime.Now.ToString() + str);
                }
            }
            catch (Exception ex)
            {
                //System.IO.File.AppendAllText(logfile, "退出登录" + DateTime.Now.ToString() + ex.Message);
            }
        }

        public DataTable 取得直播列表()
        {
            string url = $"{this.host}/live/list?token={this.token}&start=0&limit=9999"; //分页用的，直接写定

            视频流媒体管理.Code.HttpPost hp = new Code.HttpPost();

            string str = hp.GetData(url, "text/html", new Dictionary<string, string>());
            JObject ob = JObject.Parse(str);
            JArray ja = ob["rows"] as JArray;
            DataTable dt = this.摄像机列表结构();

            int 序号 = 1;
            foreach (JToken j in ja)
            {
                JObject t = j["live"] as JObject;
                DataRow dr = dt.NewRow();
                this.写直播连接信息(dr, t, 序号++);
                dt.Rows.Add(dr);
            }
            this.添加保活字段(dt);
            return dt;
            // string json = Newtonsoft.Json.JsonConvert.SerializeObject(dt);
            //return json;
        }


        public DataTable 取得直播列表_单个(string id)
        {
            //JObject p = JObject.Parse(postdat);
            //string token = p["token"].ToString();
            // string id = p["id"].ToString();

            string url = $"{this.host}/live/get?token={this.token}&id={id}"; //每次只查一个
            视频流媒体管理.Code.HttpPost hp = new Code.HttpPost();

            string str = hp.GetData(url, "text/html", new Dictionary<string, string>());
            DataTable dt = this.摄像机列表结构();

            if (str != "{}")
            {
                JObject t = JObject.Parse(str);

                int 序号 = 1;
                DataRow dr = dt.NewRow();
                this.写直播连接信息(dr, t, 序号);
                dt.Rows.Add(dr);
            }
            this.添加保活字段(dt);
            return dt;
            // string json = Newtonsoft.Json.JsonConvert.SerializeObject(dt);
            // return json;
        }

        public string 打开关闭直播(string postdat)
        {
            string url = $"{this.host}/live/turn/actived";
            Dictionary<string, string> dict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(postdat); // new Dictionary<string, string>();
            dict["token"] = this.token;
            // dict["id"] = id;
            //dict["actived"] = actived;

            视频流媒体管理.Code.HttpPost hp = new Code.HttpPost();
            // string contType = "";
            string cons = hp.组合FormData(dict, out string contType);
            string str = hp.PostData(url, contType, cons, new Dictionary<string, string>());
            return str; //  context.Response.Write(str); // {"code":200,"msg":"Success"}
        }

        public string 新增直播(string postdat)
        {
            Dictionary<string, string> dict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(postdat);
            dict["token"] = this.token;
            //dict["cid"] = dict["cid"].ToString();      //摄像机ID
            //dict["name"] = dict["name"].ToString();    //摄像机名
            //dict["vo[type]"] = "online";            //在线源
            //dict["vo[src]"] = dict["src"].ToString();  //源视频，如rtsp：//
            //dict["kind"] = "pull";                  //拉方式
            //dict["actived"] = "true";               //添加后开始直播，也可后续再控制

            //查查询视频编号是否存在
            DataTable dt = this.取得直播列表_单个(dict["cid"]);  //
            //string url = $"{this.host}/live/get";
            //url += "?token=" + dict["token"];
            //url += "&id=" + dict["cid"];
            //视频流媒体管理.App_Code.HttpPost hp = new App_Code.HttpPost();
            //string str = hp.GetData(url, "text/html", new Dictionary<string, string>());
            if (dt.Rows.Count != 0)
            {
                JObject ob = new JObject();
                ob["错误信息"] = "直播编号已存在:" + dict["cid"];
                return ob.ToString(); // context.Response.Write(ob.ToString()); // {"code":200,"msg":"Success"}
            }
            else
            {
                string url = $"{this.host}/live/save";

                视频流媒体管理.Code.HttpPost hp = new Code.HttpPost();
                string contType = "";
                string cons = hp.组合FormData(dict, out contType);
                string str = hp.PostData(url, contType, cons, new Dictionary<string, string>());
                return str; // context.Response.Write(str); // 
            }
        }

        /// <summary>
        /// 查询xls中的摄像机列表ID，查找摄像机信息
        /// </summary>
        /// <param name="id">摄像机id</param>
        /// <returns></returns>
        public string 新增xls直播(string id)
        {
            DataRow dr = this.查询摄像机ID(id);
            if (dr == null)
            {
                JObject ob = new JObject();
                ob["错误信息"] = "摄像机ID不存在:" + id;
                return ob.ToString(); // context.Response.Write(ob.ToString()); // {"code":200,"msg":"Success"}
            }
            else
            {
                DataTable dt = this.取得直播列表_单个(id);  //
                if (dt.Rows.Count != 0)
                {
                    string tt = Newtonsoft.Json.JsonConvert.SerializeObject(dt);
                    return tt; //正在直播中
                }
            }

            //直接中没有，需要新开直播
            JObject dict = new JObject();
            dict["cid"] = dr["摄像机ID"].ToString();
            dict["name"] = dr["摄像机名称"].ToString();    //摄像机名
            dict["vo[type]"] = "online";            //在线源
            dict["vo[src]"] = dr["直播地址"].ToString();  //源视频，如rtsp：//
            dict["kind"] = "pull";                  //拉方式
            dict["actived"] = "true";               //添加后开始直播，也可后续再控制

            string str = this.新增直播(dict.ToString());
            return str; // context.Response.Write(str); // 
        }

        public string 删除直播(string postdat)
        {
            JObject p = JObject.Parse(postdat);
            string url = $"{this.host}/live/remove?token={this.token}&id={p["id"]}";

            视频流媒体管理.Code.HttpPost hp = new Code.HttpPost();
            string str = hp.GetData(url, "text/html", new Dictionary<string, string>());
            return str; //json
        }

        public void 摄像机保活(string id)
        {
            this.dict播放列表[id] = DateTime.Now;
        }


        private DataTable 摄像机列表结构()
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("序号", typeof(int));
            dt.Columns.Add("id");
            dt.Columns.Add("name");
            dt.Columns.Add("直播开头"); //是否正在直播
            dt.Columns.Add("直播状态"); //是否正在直播
            dt.Columns.Add("src"); //源地址
            dt.Columns.Add("观众数量", typeof(int)); //观众人数
            dt.Columns.Add("HLS"); //播放地址
            dt.Columns.Add("HTTP_FLV"); //播放地址
            dt.Columns.Add("创建时间",typeof(DateTime)); 
            dt.Columns.Add("保活时间", typeof(DateTime));
            return dt;
        }

        private void 写直播连接信息(DataRow dr, JObject ob, int 序号)
        {
            dr["序号"] = 序号;
            dr["id"] = ob["id"];
            dr["name"] = ob["name"];
            dr["直播开头"] = ob["actived"];
            dr["src"] = ob["source"];
            dr["直播状态"] = ob["liveType"];
            dr["创建时间"] = ob["createAt"];
            dr["保活时间"] = ob["createAt"]; //和创建时间一样，后面再写入
            JObject s = ob["session"] as JObject;
            if (s != null)
            {
                dr["HLS"] = this.host + s["HLS"];
                dr["HTTP_FLV"] = this.host + s["HTTP-FLV"];
                dr["观众数量"] = s["NumOutputs"]; //播放hls时不算观众数量，不知为何
            }
        }

        private void 添加保活字段(DataTable dt)
        {
            foreach (DataRow dr in dt.Rows)
            {
                string id = dr["id"].ToString();
                if (this.dict播放列表.TryGetValue(id, out DateTime t))
                {
                    dr["保活时间"] = t;
                }
            }
        }

        public string 生成MD5(string 明文)
        {
            MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
            byte[] buf = Encoding.UTF8.GetBytes(明文);
            byte[] data = md5Hasher.ComputeHash(buf);
            StringBuilder sBuilder = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }
            return sBuilder.ToString();

        }

        private DataRow 查询摄像机ID(string 摄像机ID)
        {
            foreach (DataRow dr in this.dt摄像机列表.Rows)
            {
                if (dr["摄像机ID"].ToString().ToUpper() == 摄像机ID.ToUpper())
                    return dr;
            }
            return null;
        }

    }
}